using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;

namespace Pixysoft.Reflection
{
    public class FieldReflector
    {
        public static bool Contains(object obj, string field)
        {
            lock (typeof(FieldReflector))
            {
                if (obj == null || field == null)
                    return false;

                return Contains(obj.GetType(), field);
            }
        }

        private static bool Contains(Type type, string field)
        {
            if (type.GetField(field, BindingFlags.NonPublic | BindingFlags.Instance) != null)
                return true;
            else if (type.BaseType.Name.ToUpper() != "OBJECT" && type.BaseType.Name.ToUpper() != "VALUETYPE")
                return Contains(type.BaseType, field);
            else
                return false;

        }

        public static U GetValue<T, U>(T obj, string field)
        {
            lock (typeof(FieldReflector))
            {
                if (obj == null || field == null)
                    return default(U);

                Type type = obj.GetType();

                return GetValue<U>(type, obj, field);
            }
        }

        public static U GetValue<U>(object obj, string field)
        {
            lock (typeof(FieldReflector))
            {
                if (obj == null || field == null)
                    return default(U);

                Type type = obj.GetType();

                return GetValue<U>(type, obj, field);
            }
        }

        public static object GetValue(object obj, string field)
        {
            lock (typeof(FieldReflector))
            {
                if (obj == null || field == null)
                    return null;

                Type type = obj.GetType();

                return GetValue(type, obj, field);
            }
        }

        private static U GetValue<U>(Type type, object obj, string field)
        {
            FieldInfo info = type.GetField(field, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);

            if (info == null)
                if (type.BaseType.Name.ToUpper() != "OBJECT" && type.BaseType.Name.ToUpper() != "VALUETYPE")
                {
                    return GetValue<U>(type.BaseType, obj, field);
                }
                else
                {
                    return default(U);
                }

            try
            {
                object value = info.GetValue(obj);

                return (U)value;
            }
            catch
            {
                return default(U);
            }
        }

        private static object GetValue(Type type, object obj, string field)
        {
            FieldInfo info = type.GetField(field, BindingFlags.NonPublic | BindingFlags.Instance);

            if (info == null)
                if (type.BaseType.Name.ToUpper() != "OBJECT" && type.BaseType.Name.ToUpper() != "VALUETYPE")
                {
                    return GetValue(type.BaseType, obj, field);
                }
                else
                {
                    return null;
                }

            try
            {
                object value = info.GetValue(obj);

                return value;
            }
            catch
            {
                return null;
            }
        }

        public static bool SetValue(object obj, string field, object value)
        {
            lock (typeof(FieldReflector))
            {
                if (obj == null || field == null)
                    return false;

                Type type = obj.GetType();

                return SetValue(type, obj, field, value);
            }
        }

        private static bool SetValue(Type type, object obj, string field, object value)
        {
            FieldInfo info = type.GetField(field, BindingFlags.NonPublic | BindingFlags.Instance);

            if (info == null)
            {
                if (type.BaseType.Name.ToUpper() != "OBJECT" && type.BaseType.Name.ToUpper() != "VALUETYPE")
                {
                    return SetValue(type.BaseType, obj, field, value);
                }
                else
                {
                    return false;
                }
            }

            try
            {
                info.SetValue(obj, value);

                return true;
            }
            catch
            {
                return false;
            }
        }

    }
}
